import Base from '@/common/cmdBase/cmdBase';
import { lineManager, LineType, pointsManager } from '@/common/preview';
import { useDocumentStore } from '@/common/store';
import { EntityInfo, EntityPartType, THREE } from '@amcax/web-render';
import { handleSnapPoint, snapPoint } from '../../util';

export default class CmdInteractor extends Base {
  idxList: number[] = []; // 面片索引数组
  pointsList: THREE.Vector3Tuple[] = []; // 点数组
  vertexPoint: THREE.Vector3Tuple; //线端点-->吸附线上的点用作绘制线时当pointsList第一个点
  mouseState: boolean = false; // 当前鼠标状态
  setIsFormDataValid: (state: boolean) => void; // 更改确定按钮状态
  constructor(setIsFormDataValid: (state: boolean) => void) {
    super();
    this.setIsFormDataValid = setIsFormDataValid;
    this.init();
    this.registerEvents();
  }

  // 初始化
  init() {
    this.interactionMgr.pickFirstOnly = true; // 只拾取第一个，提高交互性能
    this.interactionMgr.pickType = [
      EntityPartType.element,
      EntityPartType.line,
    ];
    this.interactionMgr.enableBoxSelect = false;
    lineManager().setAssisMgr(this.assisMgr);
    // 打开拾取状态
    this.interactionMgr.enablePick = true;
  }

  // 鼠标左键事件
  onMouseLeft = async (e: EntityInfo) => {
    this.mouseState = false;
    this.showPoint(e);
    this.handleAdsorptionPoint(e);
  };

  /**
   * 在hover之前，判断如果hover的不是element，则能拾取；
   * 如果hover的是element，则只能拾取 workingMeshObject 网格的element
   */
  onBeforeHovered = (e: EntityInfo) => {
    return (
      e.type !== EntityPartType.element ||
      e?.dxid === useDocumentStore.getState().workingMeshObject
    );
  };

  // 鼠标盘旋绘制点
  onHovered = (hoverInfo: EntityInfo) => {
    if (!hoverInfo?.point) {
      return;
    }
    /*
     * mouseState为true长按左键开始进行绘制线，否则false
     * vertexPoint 记录吸附分割线的端点
     * pointsList 记录绘制线的数据
     * idxList记录点所在网格单元idx
     * pointsList的长度/3等于idxList长度
     * */
    if (this.mouseState) {
      this.handleAdsorptionPoint(hoverInfo);
    } else {
      this.showPoint(hoverInfo);
    }
  };

  // 高亮拾取线上的点
  showPoint = (hoverInfo: EntityInfo) => {
    /*
     * 当mouseState处于false的时候如果拾取到的是线，则进行高亮线上的点
     * */
    if (hoverInfo.type === EntityPartType.line) {
      // 获取吸附点的信息
      this.vertexPoint = snapPoint(hoverInfo);
      if (this.vertexPoint === null) {
        return;
      }
      // 绘制吸附线上点
      pointsManager().renderMovePoints([this.vertexPoint]);
    } else {
      // 拾取不是线则清除绘制的点
      pointsManager().removePoints();
    }
  };

  // 如果有吸附到的端点，则根据吸附的点，获取该点所能hover的单元信息
  handleAdsorptionPoint = (hoverInfo: EntityInfo) => {
    // 绘制时进行绘制线
    this.showPoint(hoverInfo);
    // 端点如果有数据则将端数据存在绘制线的数组里。以及对应的网格单元idx
    if (this.vertexPoint) {
      hoverInfo = handleSnapPoint(this.vertexPoint);
      // 使用完端点进行置空
      this.vertexPoint = null;
      if (!hoverInfo) {
        return;
      }
    }
    const { idx, point, type, dxid } = hoverInfo;
    // 端点无数据则将hoverinfo记录的点数据存进绘制线里，因为绘制线不需要都存线的端点
    this.pointsList.push(point);
    // 当绘制线使用hoverinfo记录的点数据时候则对应的单元id也使用hoverinfo的数据
    this.idxList.push(idx);
    if (this.pointsList.length >= 2) {
      // 先清除线，再释放线
      lineManager().clearLine(LineType.CURVE);
      lineManager().createUpdateCurve(this.pointsList, {}, LineType.CURVE);
      this.setIsFormDataValid(true);
      // 渲染刷新
      this.mainRenderer.render();
    }
  };

  // 监听window鼠标离开
  onMouseupWindow = (e: MouseEvent) => {
    this.mouseState = false;
  };

  // 鼠标点击事件监听
  mouseEvent = () => {
    this.mainRenderer.container['addEventListener'](
      'mousedown',
      this.onMouseDownPro,
    );
    window.addEventListener('mouseup', this.onMouseupWindow);
    window.addEventListener('keyup', this.onKeyup);
  };

  // 键盘监听事件
  onKeyup = (ev: KeyboardEvent) => {
    if (ev.ctrlKey && ev.key == 'z') {
      // ctrl+z 撤销
      this.pointsList = [];
      this.idxList = [];
      this.mouseState = false;
      const curve = lineManager().getLine(LineType.CURVE);
      if (curve) {
        lineManager().clearLine(LineType.CURVE);
      }
      this.setIsFormDataValid(false);
      // 渲染刷新
      this.mainRenderer.render();
    }
  };

  // 鼠标点击事件
  onMouseDownPro = () => {
    // 点击重新绘制线时候清除点信息和线的数据
    this.idxList = [];
    this.pointsList = [];
    this.mouseState = true;
    const curve = lineManager().getLine(LineType.CURVE);
    if (curve) {
      lineManager().clearLine(LineType.CURVE);
    }
    this.setIsFormDataValid(false);
  };

  // 释放资源
  release = () => {
    this.interactionMgr.pickFirstOnly = false; // 重置只拾取第一个
    this.clear();
    this.pointsList = [];
    this.mouseState = false;
    const curve = lineManager().getLine(LineType.CURVE);
    if (curve) {
      lineManager().clearLine(LineType.CURVE);
    }
    this.setIsFormDataValid(false);
  };

  // 获取点的信息
  getDataInfo = () => {
    return {
      fidx: this.idxList,
      point: this.pointsList,
    };
  };
}
